/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.faimg.pomar.modelo.dao;

import br.faimg.pomar.modelo.pojo.NumeroSerie;
import br.faimg.pomar.modelo.pojo.Producao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author flavio
 */
public class ProducaoDAO {

    public void create(Producao pojo) throws Throwable {

        
        Connection con = null;
        PreparedStatement stm = null;
        String sql = "insert into producao values(nextval('pdc_id_seq')," +
                "?,?,?,CURRENT_DATE,CURRENT_TIME);";
        
        verificaSeProdutoSucateado(pojo.getNumerodeSerie().getNumSerie());
        
        existeRotaParaProdutoLido(pojo);
        
        estacaoCorretaDaSequencia(pojo).equals(pojo.getEstacao().getCodigo().toString());
        

        try {

            con = ConnectionManager.getInstance().getConnection();
            stm = con.prepareStatement(sql);
            stm.setString(1, pojo.getNumerodeSerie().getNumSerie());
            stm.setString(2, pojo.getEstacao().getCodigo());
            stm.setString(3, "000000");
            System.out.println("ProducaoDAO::create() : " + stm.toString());
            stm.execute();


        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {

            if (stm != null) {

                try {
                    stm.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }


            if (con != null) {

                try {
                    con.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }
    }

    public void createProdutoComErro(Producao pojo) throws Throwable {
        Connection con = null;
        PreparedStatement stm = null;
        String sql = "insert into producao values(nextval('pdc_id_seq')," +
                "?,?,?,CURRENT_DATE,CURRENT_TIME);";

//        if ( existeRotaParaProdutoLido(pojo) && 
//                estacaoCorretaDaSequencia(pojo).
//                equals(pojo.getEstacao().getCodigo().toString()) ) {
        verificaSeProdutoSucateado(pojo.getNumerodeSerie().getNumSerie());
        existeRotaParaProdutoLido(pojo);
        estacaoCorretaDaSequencia(pojo).equals(pojo.getEstacao().getCodigo().toString());

        try {

            con = ConnectionManager.getInstance().getConnection();
            stm = con.prepareStatement(sql);
            stm.setString(1, pojo.getNumerodeSerie().getNumSerie());
            stm.setString(2, pojo.getEstacao().getCodigo());
            stm.setString(3, pojo.getTipodeErro().getCodigo());
            System.out.println("ProducaoDAO::createProdutoComErro() : " + stm.toString());
            stm.execute();


        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {

            if (stm != null) {

                try {
                    stm.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }


            if (con != null) {

                try {
                    con.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }
    }

    public void createProdutoReparo(Producao pojo) throws Throwable {

        verificaSeProdutoSucateado(pojo.getNumerodeSerie().getNumSerie());
        
        if (verificaSeProdutoEmReparo(pojo)) {

            Connection con = null;
            PreparedStatement stm = null;
            String sql = "insert into producao values(nextval('pdc_id_seq')," +
                    "?,?,?,CURRENT_DATE,CURRENT_TIME);";

            existeRotaParaProdutoLido(pojo);
            estacaoCorretaDaSequencia(pojo).equals(pojo.getEstacao().getCodigo().toString());

            try {

                con = ConnectionManager.getInstance().getConnection();
                stm = con.prepareStatement(sql);
                stm.setString(1, pojo.getNumerodeSerie().getNumSerie());
                stm.setString(2, pojo.getEstacao().getCodigo());
                stm.setString(3, pojo.getTipodeErro().getCodigo());
                System.out.println("ProducaoDAO::createProdutoComErro() : " + stm.toString());
                stm.execute();


            } catch (SQLException ex) {
                Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
            } finally {

                if (stm != null) {

                    try {
                        stm.close();
                    } catch (SQLException ex) {
                        Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                    }

                }


                if (con != null) {

                    try {
                        con.close();
                    } catch (SQLException ex) {
                        Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        } else {
            throw  new Throwable("Este produto nao está em reparo!");
        }
    }

    /**
     * Retorna a sequenciaAtual do produto lido na estacao.
     * Verifica somente as sequencias sem erro. As leituras com erro
     * são desconsideradas.
     * 
     * Sequência 0 indica início da rota.
     * Sequências de número repetidas indicam caminhos alternativos.
     * 
     * @param pojo
     * @return -1 Quando parametro nulo.
     * @return >= 0 quando a sequenciaAtual esta correta, sendo que o numero
     *  retornado é o numero da sequenciaAtual atual.
     * @return -2 quando nao existe sequenciaAtual para o produto lido.
     */
    public Integer sequenciaAtual(Producao pojo) throws Throwable {
        Integer sequencia = new Integer(-2);
        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;

        String sql = "select count(*) from producao where pdc_nus_numero = ? " +
                "and pdc_trr_codigo = '000000'";

        if (pojo == null) {

            throw new Throwable("Numero de série do produto é nulo, verifique!");

        } else {
            if (existeRotaParaProdutoLido(pojo)) {
                /*  
                 *  Se entrou aqui é porque existe uma rota para o produto e 
                 * agora serão feitas as validações:
                 *  1 - Se e a primeira leitura do produto na linha.
                 *  2 - Se o produto está na estação correta.
                 */


                con = ConnectionManager.getInstance().getConnection();
                try {
                    stm = con.prepareStatement(sql);
                    stm.setString(1, pojo.getNumerodeSerie().getNumSerie().toString());
                    rs = stm.executeQuery();
                    //System.out.println(rs.getStatement());


                    while (rs.next()) {
                        sequencia = new Integer(rs.getInt(1));

                    //System.out.println("\t Sequencia: dentro do metodo -> " + rs.getInt(1));
                    }

                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }

        return sequencia;

    }

    
    
    
    /**
     * Verifica se existe rota para o numero de serie lido na estacao
     * @param Producao pojo
     * @return true no caso de o produto lido ter uma rota configurada
     */
    public Boolean existeRotaParaProdutoLido(Producao pojo) throws Throwable {
        Boolean rotaproduto = new Boolean(false);

        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;

        String sql = "select count(rtp_codigo) " +
                "from rota_produto where rtp_orp_numero = " +
                "(select nus_orp_numero from num_serie where nus_numero = ?)";

        if (pojo != null) {
            con = ConnectionManager.getInstance().getConnection();
            try {
                stm = con.prepareStatement(sql);
                stm.setString(1, pojo.getNumerodeSerie().getNumSerie());
                rs = stm.executeQuery();

                //System.out.println("existeRotaParaProdutoLido() : " + rs.getStatement());

                while (rs.next()) {
                    int qtdRota = rs.getInt("count");

                    if (qtdRota == 1) {

                        rotaproduto = new Boolean(true);
                    } else {
                        throw new Throwable("Nao existe rota para o " +
                                "seguinte produto: " +
                                pojo.getNumerodeSerie().getNumSerie() +
                                "\nFavor fazer o apontamento de um produto correto");
                    }
                }

            } catch (SQLException ex) {
                Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

        return rotaproduto;
    }

    
    
    /**
     * Caso a estacao de teste leia um produto que complete uma caixa, será
     * exibida uma mensagem que a caixa deve ser fechada.
     * 
     * @param numeroSerie
     * @param codigoEstacao
     * @throws java.lang.Throwable
     */
    public void verificaSeCaixaDeveSerFechada(String numerodeSerie, String codigoEstacao) throws Throwable{
        
        Producao pojo = new Producao();
        NumeroSerie npojo = new NumeroSerie();
        npojo.setNumSerie(numerodeSerie);
        pojo.setNumerodeSerie(npojo);
        
        int sequenciaAtual = sequenciaAtual(pojo).intValue();
        int sequenciaFinal = sequenciaFinal(pojo).intValue();
        
        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;
        String sql = "select (select pdt_qtdemb from num_serie,ordem_producao,produto where nus_numero = ? and nus_orp_numero = orp_numero and orp_pdt_codigo = pdt_codigo) as pdt_qtdemb, count(*) as qtd_produzida from producao where pdc_trr_codigo = '000000' and pdc_est_codigo = ?";

        con = ConnectionManager.getInstance().getConnection();
        
        int quantidadeEmbalagem = -1;
        int quantidadeProduzida = -1;
        

        try {
            stm = con.prepareStatement(sql);
            stm.setString(1, numerodeSerie);
            //stm.setString(2, numerodeSerie);
            stm.setString(2, codigoEstacao);
            
            System.out.println("verificaSeCaixaDeveSerFechada(): " + stm.toString());

            rs = stm.executeQuery();


            while (rs.next()) {
                
                quantidadeEmbalagem = rs.getInt(1);
                quantidadeProduzida = rs.getInt(2);
                
            }


        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {

            if (stm != null) {

                try {
                    stm.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            if (con != null) {

                try {
                    con.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }

        if ( quantidadeProduzida > 0 && (quantidadeProduzida%quantidadeEmbalagem) == 0 ) {

            throw  new Throwable("Caixa completa!\nFavor fechar a caixa e identificá-la corretamente.\n Caixa número: " + (quantidadeProduzida/quantidadeEmbalagem) );

        } 
        
    }

    
    
    private Boolean estacaoCorretaNaSequenciaQuandoEmReparo(Producao pojo) throws Throwable {
        Boolean estacao = new Boolean(false);

        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;

        String sql = "select rte_est_codigo_pro from rota_estacao,estacao where rte_rtp_codigo = (select rtp_codigo from rota_produto where rtp_orp_numero = (select nus_orp_numero from num_serie where nus_numero = ? )) and rte_sequencia = (select count(*) from producao where pdc_nus_numero = ? and pdc_trr_codigo = '000000') + 1 and rte_est_codigo_pro = est_codigo and est_tpe_codigo = 'TER'";
        
        con = ConnectionManager.getInstance().getConnection();

        try {
            stm = con.prepareStatement(sql);

            stm.setString(1, pojo.getNumerodeSerie().getNumSerie());
            stm.setString(2, pojo.getNumerodeSerie().getNumSerie());


            rs = stm.executeQuery();
            //System.out.println("estacaoCorretaNaSequenciaQuandoOK() : " + rs.getStatement());
            while (rs.next()) {
                String estacaoTeste = rs.getString("rte_est_codigo_pro");
                if (estacaoTeste.equals(pojo.getEstacao().getCodigo())) {
                    estacao = new Boolean(true);
                } else {
                    throw new Throwable("Estacao incorreta!\nO produto deve passar pela estação " + estacaoTeste);
                }

            }

        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        }

        return estacao;
    }

    
    
    /**
     * Verifica se existe sequenciaAtual para o numero de série lido e true
     * quando a estação que fez a pergunta é a estação correta da rota.
     * 
     * @param sequenciaAtual - sequenciaAtual atual da produção
     * @param pojo - do tipo Produção
     * @return Um objeto do tipo String contendo o nome da estação.
     */
    private Boolean estacaoCorretaNaSequenciaQuandoOK(Producao pojo) throws Throwable {
        Boolean estacao = new Boolean(false);

        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;

        String sql = "select rte_est_codigo_pro from rota_estacao,estacao where rte_rtp_codigo = (select rtp_codigo from rota_produto where rtp_orp_numero = (select nus_orp_numero from num_serie where nus_numero = ? )) and rte_sequencia = (select count(*) from producao where pdc_nus_numero = ? and pdc_trr_codigo = '000000') and rte_est_codigo_pro = est_codigo and est_tpe_codigo != 'TER'";

        int sequenciaAtual = sequenciaAtual(pojo).intValue();
        int sequenciaFinal = sequenciaFinal(pojo).intValue();

        if (sequenciaAtual < 0) {
            throw new Throwable("Sequencia inexixtente para este produto!");
        }

        if (sequenciaAtual >= sequenciaFinal) {
            throw new Throwable("Sequencia finalizada para este produto!");
        }

        con = ConnectionManager.getInstance().getConnection();

        try {
            stm = con.prepareStatement(sql);

            stm.setString(1, pojo.getNumerodeSerie().getNumSerie());
            stm.setString(2, pojo.getNumerodeSerie().getNumSerie());


            rs = stm.executeQuery();
            //System.out.println("estacaoCorretaNaSequenciaQuandoOK() : " + rs.getStatement());
            while (rs.next()) {
                String estacaoTeste = rs.getString("rte_est_codigo_pro");
                if (estacaoTeste.equals(pojo.getEstacao().getCodigo())) {
                    estacao = new Boolean(true);
                } else {
                    throw new Throwable("Estacao incorreta!\nO produto deve passar pela estação " + estacaoTeste);
                }

            }

        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        }

        return estacao;
    }

   
    
    private Boolean estacaoCorretaDaSequenciaQuandoErro(Producao pojo) throws Throwable {

        Boolean estacao = new Boolean(false);

        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;

        String sql = "select rte_est_codigo_pro from rota_estacao,estacao where rte_rtp_codigo = (select rtp_codigo from rota_produto where rtp_orp_numero = (select nus_orp_numero from num_serie where nus_numero = ? )) and rte_sequencia = (select count(*) from producao where pdc_nus_numero = ? and pdc_trr_codigo = '000000') and rte_est_codigo_pro = est_codigo and est_tpe_codigo = 'TER'";

        int sequenciaAtual = sequenciaAtual(pojo).intValue();
        int sequenciaFinal = sequenciaFinal(pojo).intValue();

        if (sequenciaAtual < 0) {
            throw new Throwable("Sequencia inexixtente para este produto!");
        }

        if (sequenciaAtual >= sequenciaFinal) {
            throw new Throwable("Sequencia finalizada para este produto!");
        }

        con = ConnectionManager.getInstance().getConnection();

        try {
            stm = con.prepareStatement(sql);

            stm.setString(1, pojo.getNumerodeSerie().getNumSerie());
            stm.setString(2, pojo.getNumerodeSerie().getNumSerie());


            rs = stm.executeQuery();
            //System.out.println("estacaoCorretaNaSequenciaQuandoOK() : " + rs.getStatement());
            while (rs.next()) {
                String estacaoTeste = rs.getString("rte_est_codigo_pro");
                if (estacaoTeste.equals(pojo.getEstacao().getCodigo())) {
                    estacao = new Boolean(true);
                } else {
                    throw new Throwable("Estacao incorreta!\nO produto deve passar pela estação " + estacaoTeste);
                }

            }

        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        }

        return estacao;

    }

    /**
     * Verifica se a leitura foi realizada com ou sem erro e decide qual metodo
     * invocar para validação da leitura realizada por uma estação na 
     * produção.
     * @param pojo
     * @return
     */
    public Boolean estacaoCorretaDaSequencia(Producao pojo) throws Throwable {

        boolean emReparo = verificaSeProdutoEmReparo(pojo).booleanValue();
        
        if (emReparo) {
            
                return estacaoCorretaNaSequenciaQuandoEmReparo(pojo);
                
        } else {

            //Entrada de um produto com erro.
            if (pojo.getTipodeErro().getCodigo().startsWith("E")) {

                return estacaoCorretaDaSequenciaQuandoErro(pojo);

            }

            //entrada de produtos sem erro
            if (pojo.getTipodeErro().getCodigo().equals("000000")) {

                return estacaoCorretaNaSequenciaQuandoOK(pojo);

            }
        }
        
        
        throw new Throwable("Erro na checagem da estacao da sequencia ou do código!\n Caso nao previsto no metodo estacaoCorretaDaSequencia()");

    }

    private Integer sequenciaFinal(Producao pojo) throws Throwable {
        // 0 quando nao exite rota para o produto indicado.
        Integer sequencia = new Integer(0);
        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;

        String sql = "select max(rte_sequencia) from rota_estacao where rte_rtp_codigo = (select rtp_codigo from rota_produto where rtp_orp_numero = (select nus_orp_numero from num_serie where nus_numero = ?))";

        if (pojo == null) {

            throw new Throwable("Numero de série do produto é nulo, verifique!");

        } else {
            if (existeRotaParaProdutoLido(pojo)) {
                /*  
                 *  Se entrou aqui é porque existe uma rota para o produto e 
                 * agora serão feitas as validações:
                 *  1 - Se e a primeira leitura do produto na linha.
                 *  2 - Se o produto está em produção.
                 */


                con = ConnectionManager.getInstance().getConnection();
                try {
                    stm = con.prepareStatement(sql);
                    stm.setString(1, pojo.getNumerodeSerie().getNumSerie().toString());
                    rs = stm.executeQuery();
                    //System.out.println(rs.getStatement());


                    while (rs.next()) {
                        sequencia = new Integer(rs.getInt(1));

                    //System.out.println("\t Sequencia: dentro do metodo -> " + rs.getInt(1));
                    }

                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }

        return sequencia;

    }
    
    
    /**
     * Verifica se o produto esta em manutenção. Caso esteja lança um exceção
     * @param numeroDeSerie
     * @return true quando produto em reparo
     */
    public Boolean verificaSeProdutoEmReparo(Producao pojo){
        Connection con = null;
        PreparedStatement reparoStm = null;
        PreparedStatement erroStm = null;
        
        ResultSet reparoResultSet = null;
        ResultSet erroResultSet = null;
        
        Boolean retorno = null;

        String reparoSql = "select count(*) from producao where pdc_nus_numero = ? and pdc_trr_codigo like 'R%'";
        String erroSql = "select count(*) from producao where pdc_nus_numero = ? and pdc_trr_codigo like 'E%'";
        
        //Quantas vezes o dito cujo (vulgo produto) foi para o reparo
        int emReparo = -1;
        //Quantas vezes o danado saiu do reparo
        int comErro = -1;
        

        con = ConnectionManager.getInstance().getConnection();
        try {
            reparoStm = con.prepareStatement(reparoSql);
            reparoStm.setString(1, pojo.getNumerodeSerie().getNumSerie());
            
            erroStm = con.prepareStatement(erroSql);
            erroStm.setString(1, pojo.getNumerodeSerie().getNumSerie());
            
            reparoResultSet = reparoStm.executeQuery();
            erroResultSet = erroStm.executeQuery();
            
            while (reparoResultSet.next()) {
                
                emReparo = reparoResultSet.getInt(1);
                
            }
            
            
            while (erroResultSet.next()) {
                
                comErro = erroResultSet.getInt(1);
                
            }
            

        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {

            if (reparoStm != null) {

                try {
                    reparoStm.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }
            
            if (erroStm != null) {

                try {
                    erroStm.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

            if (con != null) {

                try {
                    con.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }
        
        if (comErro > emReparo){
            
            retorno = new Boolean(true);
            
        } else {
            
            retorno = new Boolean(false);
            
        }
           
        
        return retorno;
        
    }

    /**
     * Verifica se o produto foi sucateado. Caso sim, lança uma 
     * exceção no sistema para paralisar qualquer leitura de produto
     * @param numerodeSerie
     * @throws java.lang.Throwable
     */
    public void verificaSeProdutoSucateado(String numerodeSerie) throws Throwable {
        Connection con = null;
        PreparedStatement stm = null;
        ResultSet rs = null;
        String sql = "select * from producao,tipo_erro where pdc_nus_numero = ? and pdc_trr_codigo like 'S%' and trr_codigo = pdc_trr_codigo";

        con = ConnectionManager.getInstance().getConnection();
        
        String sucataDescricao = null;

        try {
            stm = con.prepareStatement(sql);
            stm.setString(1, numerodeSerie);
            
            System.out.println("verificaSeProdutoSucateado(): " + stm.toString());

            rs = stm.executeQuery();


            while (rs.next()) {
                
                sucataDescricao = rs.getString("trr_descricao");
            }


        } catch (SQLException ex) {
            Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {

            if (stm != null) {

                try {
                    stm.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            if (con != null) {

                try {
                    con.close();
                } catch (SQLException ex) {
                    Logger.getLogger(ProducaoDAO.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        }

        if ( sucataDescricao != null) {

            throw  new Throwable("o produto " + numerodeSerie +" foi sucatedado pelo motivo: \n\t" + sucataDescricao );

        } 

    }
    
    
}
